function CachedDtoProxy(dtoProxy) {
    this._dtoProxy = dtoProxy;
    this._monthCache = new MonthCache();
    this._travelCache = new TravelCache();
    this._preLoadTokens = [];
}

CachedDtoProxy.prototype = {
    preloadYear: function(year) {
        if (this._preLoadTokens.contains(year)) {
            return;
        }

        this._preLoadTokens.push(year);
        if (!this._monthCache.hasCacheForYear(year)) {
            this._preloadMonthsAndTravels(year);
        }
    },
    _removeToken: function(year) {
        this._preLoadTokens = this._preLoadTokens.where(function(element) {
            return element != year;
        });
    },
    _preloadMonthsAndTravels: function(year) {
        var me = this;
        this._dtoProxy.getYearAndTravelAsync(year, function(yearAndTravelDto) {
            try {
                me.putMonthAndTravelsToCache(year, yearAndTravelDto);
            } finally {
                me._removeToken(year);
            }
        });
    },
    
    putMonthAndTravelsToCache:function(year, yearAndTravelDto){
        this._travelCache.cache(year, yearAndTravelDto.travel);
        this._monthCache.cache(year, yearAndTravelDto.year);     
    },

    getMonthByYearMonth: function(yearMonth) {
        return this._getMonthsInYear(yearMonth.year())[yearMonth.month()];
    },

    _getMonthsInYear: function(year) {
        if (!this._monthCache.hasCacheForYear(year)) {
            var yearTravel = this._dtoProxy.getYearAndTravel(year);

            var yearDto = yearTravel.year;
            this._monthCache.cache(year, yearDto);

            var travelStepDtos = yearTravel.travel;
            this._travelCache.cache(year, travelStepDtos);
        }
        return this._monthCache.getMonthDtoByYear(year);
    },

    flushMonths: function() {
        var updatedMonths = this._monthCache.getUpdatedMonths();
        if (updatedMonths.length > 0) {
            this._dtoProxy.saveMonths(updatedMonths);
            this._monthCache.flush();
        }
    },

    markAsChanged: function(monthDtos) {
        var me = this;
        monthDtos.foreach(function(monthDto) {
            me._monthCache.update(monthDto);
        });
    },

    getTravelStepById: function(tid) {
        return this._travelCache.getTravelStepById(tid);
    },

    getTravelSteps: function(year) {
        if (!this._travelCache.getTravelStepDtoByYear(year)) {
            var travelDtos = this._dtoProxy.getTravelSteps(year);
            this._travelCache.cache(year, travelDtos);
        }
        return this._travelCache.getTravelStepDtoByYear(year);
    },
    saveTravelSteps: function(travelStepDtosToAdd, travelStepsToRemove) {
        var me = this;
        this._dtoProxy.saveTravelSteps(travelStepDtosToAdd, travelStepsToRemove);
        travelStepDtosToAdd.foreach(function(stepDto) {
            me._travelCache.addTravelStepDto(stepDto);
        });
        travelStepsToRemove.foreach(function(stepTid) {
            me._travelCache.removeTravelStepDto(stepTid);
        });
    }
};
var MonthCache = function() {
    this._cache = {};
    this._updatedMonths = [];
};
MonthCache.prototype = {
    cache: function(year, monthDtos) {
        var me = this;
        if (!this._cache[year]) {
            this._cache[year] = {};
        }
        monthDtos.foreach(function(monthDto, index) {
            me._cache[year][index + 1] = monthDto;
        });
    },

    getMonthDtoByYear: function(year) {
        return this._cache[year];
    },

    hasCacheForYear: function(year) {
        return !!this._cache[year];
    },

    update: function(monthDto) {
        if (this._cache[monthDto.year] === null || this._cache[monthDto.year] === undefined) {
            this._cache[monthDto.year] = {};
        }

        this._cache[monthDto.year][monthDto.month] = monthDto;
        this._updatedMonths = this._updatedMonths.where(function(month) {
            return month.year !== monthDto.year || month.month !== monthDto.month;
        });
        this._updatedMonths.push(monthDto);
    },

    getUpdatedMonths: function() {
        return this._updatedMonths;
    },

    flush: function() {
        this._updatedMonths = [];
    }
};

var TravelCache = function() {
    this._cache = {};
};

TravelCache.prototype = {
    getTravelStepDtoByYear: function(year) {
        return this._cache[year];
    },

    cache: function(year, travelStepDtos) {
        this._cache[year] = travelStepDtos;
    },

    getTravelStepById: function(travelStepTid) {
        var result = null;
        for (var year in this._cache) {
            result = this._cache[year].findFirst(function(travelStep) {
                return travelStep.tid === travelStepTid;
            });
            if (result) { break; }
        }
        return result;
    },

    addTravelStepDto: function(dto) {
        var me = this;
        for(var year in this._cache){
            if (Date.parse(dto.departureDateTime).getFullYear() == year || Date.parse(dto.arrivalDateTime).getFullYear()) {
                me._cache[year].push(dto);
            }
        };
    },

    removeTravelStepDto: function(tid) {
        var me = this;
        for(var year in this._cache) {
            me._cache[year] = me._cache[year].where(function(stepDtoOfYear) {
                return stepDtoOfYear.tid !== tid;
            });
        };
    }
};
